﻿
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
namespace WorkWriter
{
    public class Holiday
    {
        private int year;
        private static readonly Regex DATE_REGEX = new Regex("^[0-9]+$");
        private Dictionary<string, _Day> dictHolidays = new Dictionary<string, _Day>();
        private static Dictionary<int, Holiday> dictCaches = new Dictionary<int, Holiday>();

        static Holiday()
        {
            Initialize();
        }

        public Holiday(int year)
        {
            this.year = year;
        }

        public static Holiday Get(int year)
        {
            if (dictCaches.ContainsKey(year)) return dictCaches[year];
            return dictCaches[year] = new Holiday(year);
        }

        public void Add(string name, string dates, string overtime)
        {
            string[] arrDates = this.GetDates(dates);
            var all = arrDates.Concat(this.GetDates(overtime)).ToArray();
            for (int i = 0; i < all.Length; i++) dictHolidays[all[i]] = new _Day { Date = all[i], Name = name, IsOvertime = (i >= arrDates.Length), IsWeekEnd = this.IsWeekEnd(all[i]) };
        }

        public bool IsWorkDay(DateTime? date = null)
        {
            if (date == null) date = DateTime.Now;
            var sd = date.Value.ToString("MMdd");
            if (dictHolidays.ContainsKey(sd)) return dictHolidays[sd].IsOvertime;//法定加班工作日。
            return !IsWeekEnd(date.Value);
        }

        public bool IsWeekEnd(DateTime value)
        {
            return value.DayOfWeek == DayOfWeek.Sunday || value.DayOfWeek == DayOfWeek.Saturday;
        }

        public bool IsWeekEnd(string value)
        {
            DateTime date;
            return DateTime.TryParseExact(year + value, "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, out date) && IsWeekEnd(date);
        }

        public int WorkDays
        {
            get
            {
                int total = DateTime.IsLeapYear(year) ? 366 : 365;
                int weekEnds = (total / 7) * 2;
                return total - (weekEnds - dictHolidays.Values.Count(v => v.IsOvertime && v.IsWeekEnd)) - dictHolidays.Values.Count(v => !v.IsOvertime && !v.IsWeekEnd); ;//法定总工作日(一年总天数 - 周末天数 + 法定加班天数)
            }
        }

        public int GetWorkDays(DateTime date)
        {
            int[] weekValues = new[] { 7, 1, 2, 3, 4, 5, 6 };
            int total = (int)((date - new DateTime(date.Year, 1, 1)).TotalDays + 1);
            DateTime firstDay = new DateTime(date.Year, 1, 1);
            var durations = (date - firstDay).TotalDays + 1;
            int weekEnds = 0;
            if (durations < 7 && (weekValues[(int)firstDay.DayOfWeek] + (int)durations) > 7) weekEnds = 1;
            else if (durations % 7 == 0) weekEnds = 1;
            else if (durations > 7) weekEnds = ((int)durations / 7) + ((weekValues[(int)firstDay.DayOfWeek] + (durations % 7 - 1)) == 7 ? 1 : 0);

            var dictValues = dictHolidays.Values.Where(v => v.Date.CompareTo(date.ToString("MMdd")) <= 0).ToList();
            return total - (weekEnds * 2 - dictValues.Count(v => v.IsOvertime && v.IsWeekEnd)) - dictValues.Count(v => !v.IsOvertime && !v.IsWeekEnd);//法定总工作日(一年总天数 - 周末天数 + 法定加班天数)
        }

        private string[] GetDates(string dates)
        {
            if (string.IsNullOrWhiteSpace(dates)) return new string[0];
            else if (DATE_REGEX.IsMatch(dates)) return new string[] { dates };

            var list = new List<string>();
            var arrDates = dates.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var e in arrDates)
            {
                if (e.IndexOf("-") > 0)
                {
                    var arr = e.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
                    var sd = new DateTime(year, int.Parse(arr[0].Substring(0, 2)), int.Parse(arr[0].Substring(2, 2)));
                    var ed = arr[1];
                    for (var curr = arr[0]; curr.CompareTo(ed) <= 0; sd = sd.AddDays(1), curr = sd.ToString("MMdd")) list.Add(curr);
                }
                else
                {
                    list.Add(e);
                }
            }
            return list.ToArray();
        }

        private static void Initialize()
        {
            var fileName = string.Empty;
            if (!(File.Exists(fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "holiday.xml")) || File.Exists(fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin\\holiday.xml")))) return;
            XmlDocument doc = new XmlDocument();
            doc.Load(fileName);
            var root = doc.DocumentElement;
            foreach (XmlNode note in root.ChildNodes)
            {
                var holiday = Holiday.Get(int.Parse(note.Attributes["year"].Value));
                foreach (XmlNode item in note.ChildNodes) holiday.Add(item.Attributes["name"].Value, item.Attributes["dates"].Value, item.Attributes["overtime"].Value);
            }
        }

        #region sub class
        class _Day
        {
            /// <summary>
            /// 节日名称
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// 日期
            /// </summary>
            public string Date { get; set; }
            /// <summary>
            /// 是否加班
            /// </summary>
            public bool IsOvertime { get; set; }
            /// <summary>
            /// 是否属于周末
            /// </summary>
            public bool IsWeekEnd { get; set; }
        }

        #endregion
    }
}